<?php
// @formatter:off
/**
 * @file WCTimer.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-07 18:03:56 +0000 (Sun, 07 Jul 2013) $
 *
 * $Id: WCTimer.php 91 2013-07-07 18:03:56Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0-BETA2/includes/WCTimer.php $
 */
// @formatter:on

/**
 * @class WCTimer This singleton class holds the logic to manage timers and impact
 * them into a database table.
 */
class WCTimer {
	/**
	 * @var WCTimer This the the singleton's instance.
	 */
	protected static $_Instance;

	/*
	 * @var int
	 */
	protected $_globalTimer = 0;
	protected $_globalTimerRunning = false;
	protected $_onShell = false;
	protected $_timers = array();
	protected $_timersRunning = array();

	protected function __construct() {
		$this->_onShell = !empty($_SERVER['SHELL']);
	}
	public function __destruct() {
	}
	/**
	 * Prevent users to clone the instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods.
	public function clearStats() {
		global $wcDefaults;

		if($wcDefaults["stats-max-time"] > 0) {
			global $wcDatabaseAccess;

			$db = WCConnection::Instance();
			$dbprefix = $wcDatabaseAccess['prefix'];

			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($db->has($stmtId)) {
				$stmt = $db->get($stmtId);
			} else {
				$query = "delete from {$dbprefix}stats_timer\n";
				$query .= "where       st_t_sdate < :sdate\n";

				$stmt = $db->prepare($stmtId, $query);
			}

			$params = array(":sdate" => date("Ymd", strtotime("-{$wcDefaults["stats-max-time"]} day", time())));

			$stmt->execute($params);
		}
	}
	public function globalTimer(&$running = null) {
		$out = -1;

		if($this->_globalTimer) {
			$out = ceil($this->_globalTimer * 1000);
			$running = $this->_globalTimerRunning;
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Global timer was never used", E_USER_ERROR);
		}

		return $out;
	}
	public function saveGlobal($caller = "no-caller") {
		global $wcDefaults;

		$this->stopGlobal();
		if($wcDefaults["stats-max-time"] > 0) {
			$this->clearStats();

			global $wcDatabaseAccess;

			$db = WCConnection::Instance();
			$dbprefix = $wcDatabaseAccess['prefix'];

			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($db->has($stmtId)) {
				$stmt = $db->get($stmtId);
			} else {
				$query = "insert\n";
				$query .= "        into {$dbprefix}stats_timer (\n";
				$query .= "                st_t_code, st_t_action, st_t_caller, st_t_group, st_t_collection,\n";
				$query .= "                st_t_tag, st_t_directory, st_t_comic, st_t_rule, st_t_maximum,\n";
				$query .= "                st_t_minimum, st_t_average, st_t_times, st_t_method, st_t_sdate)\n";
				$query .= "        values (:code, :action, :caller, :group, :collection,\n";
				$query .= "                :tag, :directory, :comic, :rule, :time,\n";
				$query .= "                :time, :time, 1, :method, date_format(now(),'%Y%m%d'))\n";
				$query .= "        on duplicate key\n";
				$query .= "                update  st_t_maximum = greatest(st_t_maximum, :time),\n";
				$query .= "                        st_t_minimum = least(st_t_minimum, :time),\n";
				$query .= "                        st_t_average = round((st_t_average + :time) / 2),\n";
				$query .= "                        st_t_times   = st_t_times + 1\n";

				$stmt = $db->prepare($stmtId, $query);
			}

			global $wcAction;
			global $wcGroup;
			global $wcCollection;
			global $wcTag;
			global $wcDirectory;
			global $wcComic;
			global $wcRule;

			$code = "UNKNOWN";
			if(!$this->_onShell) {
				$code = "{$_SERVER["SCRIPT_NAME"]}?{$_SERVER["QUERY_STRING"]}";
			} else {
				$code = $_SERVER["SCRIPT_NAME"];
			}
			$params = array(
				":code" => $code,
				":action" => $wcAction ? $wcAction : "UNKNOWN",
				":caller" => $caller,

				":group" => $wcGroup ? $wcGroup->id : null,
				":collection" => $wcCollection ? $wcCollection->id : null,
				":tag" => $wcTag ? $wcTag->id : null,
				":directory" => $wcDirectory ? $wcDirectory->id : null,
				":comic" => $wcComic ? $wcComic->id : null,
				":rule" => $wcRule ? $wcRule->id : null,

				":time" => $this->globalTimer(),
				":method" => isset($_SERVER["REQUEST_METHOD"]) ? $_SERVER["REQUEST_METHOD"] : ""
			);

			$stmt->execute($params);
		}
	}
	public function save($id, $caller = "no-caller", $code = "UNKNOWN") {
		if(isset($this->_timersRunning[$id]) && $this->_timersRunning[$id]) {
			global $wcDefaults;

			$this->stop($id);
			if($wcDefaults["stats-max-time"] > 0) {
				$this->clearStats();

				global $wcDatabaseAccess;

				$db = WCConnection::Instance();
				$dbprefix = $wcDatabaseAccess['prefix'];

				$stmt = null;
				$stmtId = __CLASS__."::".__FUNCTION__;
				if($db->has($stmtId)) {
					$stmt = $db->get($stmtId);
				} else {
					$query = "insert\n";
					$query .= "        into {$dbprefix}stats_timer (\n";
					$query .= "                st_t_code, st_t_action, st_t_caller, st_t_group, st_t_collection,\n";
					$query .= "                st_t_tag, st_t_directory, st_t_comic, st_t_rule, st_t_maximum,\n";
					$query .= "                st_t_minimum, st_t_average, st_t_times, st_t_method, st_t_sdate)\n";
					$query .= "        values (:code, :action, :caller, :group, :collection,\n";
					$query .= "                :tag, :directory, :comic, :rule, :time,\n";
					$query .= "                :time, :time, 1, :method, date_format(now(),'%Y%m%d'))\n";
					$query .= "        on duplicate key\n";
					$query .= "                update  st_t_maximum = greatest(st_t_maximum, :time),\n";
					$query .= "                        st_t_minimum = least(st_t_minimum, :time),\n";
					$query .= "                        st_t_average = round((st_t_average + :time) / 2),\n";
					$query .= "                        st_t_times   = st_t_times + 1\n";

					$stmt = $db->prepare($stmtId, $query);
				}

				global $wcAction;
				global $wcGroup;
				global $wcCollection;
				global $wcTag;
				global $wcDirectory;
				global $wcComic;
				global $wcRule;

				if(!$this->_onShell) {
					$code = "{$_SERVER["SCRIPT_NAME"]}?{$_SERVER["QUERY_STRING"]}";
				} elseif(!defined("__CRON__")) {
					$code = $_SERVER["SCRIPT_NAME"];
				}
				$params = array(
					":code" => $code,
					":action" => $wcAction ? $wcAction : "UNKNOWN",
					":caller" => $caller,

					":group" => $wcGroup ? $wcGroup->id : null,
					":collection" => $wcCollection ? $wcCollection->id : null,
					":tag" => $wcTag ? $wcTag->id : null,
					":directory" => $wcDirectory ? $wcDirectory->id : null,
					":comic" => $wcComic ? $wcComic->id : null,
					":rule" => $wcRule ? $wcRule->id : null,

					":time" => $this->timer($id),
					":method" => isset($_SERVER["REQUEST_METHOD"]) ? $_SERVER["REQUEST_METHOD"] : ""
				);

				$stmt->execute($params);
			}
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Timer '{$id}' in already running", E_USER_ERROR);
		}
	}
	public function start($id) {
		if(!isset($this->_timersRunning[$id]) || !$this->_timersRunning[$id]) {
			$this->_timersRunning[$id] = true;
			$this->_timers[$id] = microtime(true);
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Timer '{$id}' in already running", E_USER_ERROR);
		}
	}
	public function startGlobal() {
		if(!$this->_globalTimerRunning) {
			$this->_globalTimerRunning = true;
			$this->_globalTimer = microtime(true);
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Global timer already started", E_USER_ERROR);
		}
	}
	public function stop($id) {
		if(isset($this->_timersRunning[$id]) && $this->_timersRunning[$id]) {
			$this->_timersRunning[$id] = false;
			$this->_timers[$id] = microtime(true) - $this->_timers[$id];
		} elseif(!isset($this->_timersRunning[$id])) {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Timer '{$id}' was never started", E_USER_ERROR);
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Timer '{$id}' in not running", E_USER_ERROR);
		}

		return $this->timer($id);
	}
	public function stopGlobal() {
		if($this->_globalTimerRunning) {
			$this->_globalTimerRunning = false;
			$this->_globalTimer = microtime(true) - $this->_globalTimer;
		} else {
			trigger_error(__CLASS__."::".__FUNCTION__."(): Global timer is not started", E_USER_ERROR);
		}

		return $this->globalTimer($id);
	}
	public function timer($id, &$running = null) {
		$out = 0;
		$running = null;

		if(isset($this->_timersRunning[$id])) {
			$out = ceil($this->_timers[$id] * 1000);
			$running = $this->_timersRunning[$id];
		}

		return $out;
	}
	//
	// Public class methods.
	public static function Instance() {
		if(!isset(self::$_Instance)) {
			$c = __CLASS__;
			self::$_Instance = new $c();
		}

		return self::$_Instance;
	}
}
?>